/******************************************************************************************************
Copyright (C) 2013 Mestrelab Research S.L. All rights reserved.

This file is part of the Mnova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals Application, Settings, print, Str, MnUi, MessageBox*/
/*jslint plusplus: true, indent: 4, bitwise: true*/

/** */
function TNMRPeakSpectrumSim(aNucleus) {
	"use strict";
	if (aNucleus === undefined) {
		aNucleus = '1H';
	}
	this.loadSettings();
	this.fPeaks = [];
	this.fSpectrum = {};
	this.fillDefault(aNucleus);
}

/** */
function WNMRPeakSpectrumSimDialog(aSimulator) {
	"use strict";
	this.ui = Application.loadUiFile("ricares:nmrPeakSpectrumSimulation.ui");
	if (this.ui === undefined) {
		throw new Error('Unable to load UI');
	}

	this.fPeakSimulator = aSimulator;
	this.setupUi();
	this.setupConnections();
}

/** Copies the properties of aDst that are already present in aSrc
 */
function copyExistingObjectProperties(aSrc, aDst) {
	"use strict";
	var prop;
	if (aSrc !== undefined && aDst !== undefined) {
		for (prop in aDst) {
			if (aDst.hasOwnProperty(prop) && aSrc.hasOwnProperty(prop)) {
				aDst[prop] = aSrc[prop];
			}
		}
	}
}

/** Append the properties of aDst to aSrc
*/
function copyObjectProperties(aSrc, aDst) {
	"use strict";
	var prop;
	if (aSrc !== undefined && aDst !== undefined) {
		for (prop in aSrc) {
			if (aSrc.hasOwnProperty(prop)) {
				aDst[prop] = aSrc[prop];
			}
		}
	}
}

function nmrPeakSpectrumSimulation() {
	"use strict";
	var xml, peakSim, peakDiag;

	try {
		Application.checkDependencies(["NMR"]);
	} catch (ERR) {
		MessageBox.critical(ERR);
		return;
	}

	peakSim = new TNMRPeakSpectrumSim();
	peakDiag = new WNMRPeakSpectrumSimDialog(peakSim);
	if (peakDiag.exec()) {
		copyExistingObjectProperties(peakDiag.fPeakSimulator, peakSim);
		if (peakSim.fPeaks.length !== 0) {
			xml = peakSim.toXML();
//			print(xml)
			Application.nmr.syntheticPeaksSpectrum(xml);
			peakSim.saveSettings();
		}
	}
}

TNMRPeakSpectrumSim.prototype = {};
TNMRPeakSpectrumSim.cSettingsKey = "NMR Peak Spectrum Sim";

/** */
TNMRPeakSpectrumSim.cDefaultParameters = {'1H': {'from': -2, 'to': 10, 'nPoints': 32768, 'frequency': 400.0, 'lineWidth': 0.75},
					'13C': {'from': -20, 'to': 230, 'nPoints': 32768, 'frequency': 100.0, 'lineWidth': 1.5},
					'11B': {'from': -60, 'to': 100, 'nPoints': 32768, 'frequency': 125.0, 'lineWidth': 1.5},
					'15N': {'from': -500, 'to': 500, 'nPoints': 32768, 'frequency': 40.0, 'lineWidth': 1.5},
					'17O': {'from': -50, 'to': 850, 'nPoints': 32768, 'frequency': 55.0, 'lineWidth': 1.5},
					'19F': {'from': -250, 'to': 50, 'nPoints': 32768, 'frequency': 400.0, 'lineWidth': 1.5},
					'29S': {'from': -200, 'to': 850, 'nPoints': 32768, 'frequency': 80.0, 'lineWidth': 1.5},
					'31P': {'from': -500, 'to': 500, 'nPoints': 32768, 'frequency': 162.0, 'lineWidth': 1.5}
					};

/** */
TNMRPeakSpectrumSim.prototype.fillDefault = function (aNucleus) {
	"use strict";
	this.fSpectrum.nucleus = aNucleus.toString();
	copyObjectProperties(TNMRPeakSpectrumSim.cDefaultParameters[aNucleus], this.fSpectrum);
};

/** */
TNMRPeakSpectrumSim.prototype.saveSettings = function () {
	"use strict";
	var setts = new Settings(TNMRPeakSpectrumSim.cSettingsKey),
		def = TNMRPeakSpectrumSim.cDefaultParameters[this.fSpectrum.nucleus];
	copyExistingObjectProperties(this.fSpectrum, def);
	setts.setValue(this.fSpectrum.nucleus, JSON.stringify(def));
};

/** */
TNMRPeakSpectrumSim.prototype.loadSettings = function () {
	"use strict";
	var n, jsonSett, newDef,
		setts = new Settings(TNMRPeakSpectrumSim.cSettingsKey);
	for (n in TNMRPeakSpectrumSim.cDefaultParameters) {
		if (TNMRPeakSpectrumSim.cDefaultParameters.hasOwnProperty(n) && setts.contains(n)) {
			try {
				jsonSett = setts.value(n, JSON.stringify(TNMRPeakSpectrumSim.cDefaultParameters[n])).toString();
				newDef = JSON.parse(jsonSett);
				copyExistingObjectProperties(newDef, TNMRPeakSpectrumSim.cDefaultParameters[n]);
			} catch (err) {
				print("Invalid Settings: " + err.toString());
			}
		}
	}
};

/** */
TNMRPeakSpectrumSim.prototype.toXML = function () {
	"use strict";
	var mnovapeaks = {'@version': '1.0', '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', '@xsi:noNamespaceSchemaLocation': 'Mnova-Peak.xsd', 'spectrum': {'dim': [this.fSpectrum], 'solvent': ''}, 'peakList': {'peak': this.fPeaks}};
	return Str.jsonToXML({'mnova-peaks': mnovapeaks});
};

WNMRPeakSpectrumSimDialog.prototype = {};

/** */
WNMRPeakSpectrumSimDialog.prototype.exec = function () {
	"use strict";
	return this.ui.exec();
};

/** */
WNMRPeakSpectrumSimDialog.prototype.setupUi = function () {
	"use strict";
	var n, i,
		tableRows = [],
		nucleusItems = [],
		powerOf2Items = [];
	for (n in TNMRPeakSpectrumSim.cDefaultParameters) {
		if (TNMRPeakSpectrumSim.cDefaultParameters.hasOwnProperty(n)) {
			nucleusItems.push(n);
		}
	}
	for (i = 1; i < 20; ++i) {
		powerOf2Items.push(Math.pow(2, i).toString());
	}
	this.ui.widgets.fPointComboBox.items = powerOf2Items;
	this.ui.widgets.fNucleusComboBox.items = nucleusItems;

	this.ui.widgets.fNucleusComboBox.currentIndex = this.ui.widgets.fNucleusComboBox.items.indexOf(this.fPeakSimulator.fSpectrum.nucleus);
	this.setupNucleus();
	for (i = 0; i < this.fPeakSimulator.fPeaks.length; ++i) {
		tableRows.push([{'text': this.fPeakSimulator.fPeaks[i].dim[0].shift, 'flags': 1 | 2 | 32}, {'text': this.ppmToHz(this.fPeakSimulator.fPeaks[i].dim[0].width), 'flags': 1 | 2 | 32}, {'text': this.fPeakSimulator.fPeaks[i].heigth, 'flags': 1 | 2 | 32}]);
	}
	this.ui.widgets.fTableWidget.items = tableRows;
};

/** */
WNMRPeakSpectrumSimDialog.prototype.setupNucleus = function () {
	"use strict";
	this.ui.widgets.fTableWidget.horzHeaderItems = [{'text': 'Shift (ppm)'}, {'text': 'Width (Hz)'}, {'text': 'Intensity'}];
	this.ui.widgets.fPointComboBox.currentIndex = this.ui.widgets.fPointComboBox.items.indexOf(this.fPeakSimulator.fSpectrum.nPoints.toString());
	this.ui.widgets.fMHzDoubleSpinBox.value = this.fPeakSimulator.fSpectrum.frequency;
	this.ui.widgets.fToDoubleSpinBox.value = this.fPeakSimulator.fSpectrum.to;
	this.ui.widgets.fFromDoubleSpinBox.value = this.fPeakSimulator.fSpectrum.from;
	this.ui.widgets.fLineWidthDoubleSpinBox.value = this.fPeakSimulator.fSpectrum.lineWidth;
};

/** */
WNMRPeakSpectrumSimDialog.prototype.setupConnections = function () {
	"use strict";
	this.ui.widgets.fAddToolButton.clicked.connect(this, 'onAddToolButtonClicked');
	this.ui.widgets.fRemoveToolButton.clicked.connect(this, 'onRemoveToolButtonClicked');
	this.ui.widgets.fNucleusComboBox['currentIndexChanged(const QString&)'].connect(this, 'onNucleusChanged');
	this.ui.accepted.connect(this, 'onAccepted');
};

/** */
WNMRPeakSpectrumSimDialog.prototype.onAddToolButtonClicked = function () {
	"use strict";
	var curRows = this.ui.widgets.fTableWidget.items;
	curRows.push([{'text': 1, 'flags': 1 | 2 | 32}, {'text': this.ui.widgets.fLineWidthDoubleSpinBox.value.toFixed(2), 'flags': 1 | 2 | 32}, {'text': 1.0, 'flags': 1 | 2 | 32}]);
	this.ui.widgets.fTableWidget.items = curRows;
};

/** */
WNMRPeakSpectrumSimDialog.prototype.onRemoveToolButtonClicked = function () {
	"use strict";
	var i,
		curRows = this.ui.widgets.fTableWidget.items;
	for (i = 0; i < curRows.length; ++i) {
		if (curRows[i][0].isSelected) {
			curRows.splice(i, 1);
			break;
		}
	}
	this.ui.widgets.fTableWidget.items = curRows;
};

/** */
WNMRPeakSpectrumSimDialog.prototype.onNucleusChanged = function (aNucleus) {
	"use strict";
	this.fPeakSimulator.fSpectrum.nucleus = aNucleus;
	copyExistingObjectProperties(TNMRPeakSpectrumSim.cDefaultParameters[aNucleus], this.fPeakSimulator.fSpectrum);
	this.setupNucleus();
	var i,
		curRows = this.ui.widgets.fTableWidget.items;
	for (i = 0; i < curRows.length; ++i) {
		curRows[i][1].text = this.fPeakSimulator.fSpectrum.lineWidth;
	}
	this.ui.widgets.fTableWidget.items = curRows;
};

/** */
WNMRPeakSpectrumSimDialog.prototype.ppmToHz = function (aPPM) {
	"use strict";
	return aPPM * this.fPeakSimulator.fSpectrum.frequency;
};

/** */
WNMRPeakSpectrumSimDialog.prototype.HzToppm = function (aHz) {
	"use strict";
	return aHz / this.fPeakSimulator.fSpectrum.frequency;
};

/** */
WNMRPeakSpectrumSimDialog.prototype.onAccepted = function () {
	"use strict";
	this.fPeakSimulator.fSpectrum.nucleus = this.ui.widgets.fNucleusComboBox.currentText;
	this.fPeakSimulator.fSpectrum.from = this.ui.widgets.fFromDoubleSpinBox.value.toFixed(2);
	this.fPeakSimulator.fSpectrum.to = this.ui.widgets.fToDoubleSpinBox.value.toFixed(2);
	this.fPeakSimulator.fSpectrum.nPoints = parseInt(this.ui.widgets.fPointComboBox.currentText, 10);
	this.fPeakSimulator.fSpectrum.frequency = this.ui.widgets.fMHzDoubleSpinBox.value.toFixed(2);
	this.fPeakSimulator.fSpectrum.lineWidth = this.ui.widgets.fLineWidthDoubleSpinBox.value.toFixed(2);
	this.fPeakSimulator.fPeaks = [];
	var i, h, w,
		curRows = this.ui.widgets.fTableWidget.items;
	for (i = 0; i < curRows.length; ++i) {
		h = parseFloat(curRows[i][2].text);
		if (isNaN(h)) {
			h = 1.0;
		}
		w = this.HzToppm(parseFloat(curRows[i][1].text));
		if (isNaN(w)) {
			w = this.HzToppm(this.fPeakSimulator.fSpectrum.lineWidth);
		}
		this.fPeakSimulator.fPeaks.push({'heigth': h, 'dim': [{'shift': parseFloat(curRows[i][0].text), 'width': w}]});
	}
};

if (this.MnUi && MnUi.scripts_nmr) {
	MnUi.scripts_nmr.scripts_nmr_SimulatePeaksSpectrum = nmrPeakSpectrumSimulation;
}
